package net.threeple.words.filter;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

import net.threeple.words.filter.exception.TabooWordsFileNotFoundException;
import net.threeple.words.filter.exception.UnsupportedFeatureException;
/**
 * 
 * @author Wayne Hamilton
 *
 */
public class WordsFilterImpl implements WordsFilter {
	static final Logger logger = LoggerFactory.getLogger(WordsFilterImpl.class);
	private String[] tbWords;
	private Character[] initials;
	private Character[] suffixes;
	private Character[] punctuations;
	private int maxLength;
	
	public WordsFilterImpl(Set<String> tabooWords) {
		setup(Sets.newTreeSet(tabooWords));
	}
	
	public WordsFilterImpl(File file) {
		if(!file.exists()) {
			throw new TabooWordsFileNotFoundException("敏感词配置文件不存在");
		}
		Set<String> tabooWords = Sets.newTreeSet();
		try {
			BufferedReader br = new BufferedReader(new FileReader(file));
			String word = "";
			while((word = br.readLine()) != null) {
				word = word.trim();
				if(word.length() > 0 
						&& !tabooWords.contains(word)) {
					tabooWords.add(word);
				}
			}
			br.close();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		setup(tabooWords);
	}
	
	public WordsFilterImpl(String url) {
		throw new UnsupportedFeatureException("尚未支持的特性。");
	}

	private void setup(Set<String> tabooWords) {
		Set<Character> initialSet = Sets.newTreeSet();
		Set<Character> suffixSet = Sets.newTreeSet();
		for(String word : tabooWords) {
			Character initial = word.charAt(0);
			initialSet.add(initial);
			int wordLength = word.length();
			Character suffix = word.charAt(wordLength - 1);
			suffixSet.add(suffix);
			if(maxLength < wordLength) {
				maxLength = wordLength;
			}
		}
		tbWords = tabooWords.toArray(new String[0]);
		logger.debug("共配置{}个敏感词", tbWords.length);
		initials = initialSet.toArray(new Character[0]);
		suffixes = suffixSet.toArray(new Character[0]);
		String pss = ",，.。:：《》”“\"、？?！!";
		punctuations = new Character[pss.length()];
		for(int i = 0; i < pss.length(); i++) {
			punctuations[i] = pss.charAt(i);
		}
		Arrays.sort(punctuations);
	}

	@Override
	public String[] findTabooWordsFrom(String text) {
		Set<String> tbwSet = Sets.newHashSet();
		List<Integer> initialIndexes = Lists.newArrayList();
		for(int index = 0; index < text.length(); index++) { // 扫描待检索文本
			Character character = text.charAt(index);
			if(Arrays.binarySearch(punctuations, character) >= 0) {// 跳过标点符号
				continue;
			}
			boolean isInitial = Arrays.binarySearch(initials, character) >= 0;
			boolean isSuffix = Arrays.binarySearch(suffixes, character) >= 0;
			if(!isInitial && !isSuffix) { // 不包含敏感词中的任何字
				continue;
			}
			if(isInitial) { // 是敏感词首字
				initialIndexes.add(index);
			}
			if(isSuffix) { // 是敏感词尾字，触发检索
				Integer[] indexes = initialIndexes.toArray(new Integer[0]);
				if(indexes.length <= 0) {
					continue;
				}
				for(int i = (indexes.length - 1); i >= 0; i--) { // 从最近的首字位置开始组词
					if(indexes[i] == index) { // 既是首字也是尾字
//						logger.debug("既是首字也是尾字：{}", character);
						continue;
					}
					int wordLength = index - indexes[i] + 1;
					if(wordLength > maxLength) { // 疑似敏感词超过最长长度
//						logger.debug("超长的词：{}", text.substring(indexes[i], index + 1));
						initialIndexes.removeAll(initialIndexes.subList(0, i));
						break;
					}
					String word = text.substring(indexes[i], index + 1);
//					logger.debug("发现的疑似敏感词：{}", word);
					if(Arrays.binarySearch(tbWords, word) >= 0) { // 发现的敏感词
//						logger.debug("发现的敏感词：{}", word);
						tbwSet.add(word);
					}
				}
//				logger.debug("当前数首字集合情况：{}", initialIndexes);
			}
		}
		return tbwSet.toArray(new String[0]);
	}
	
}
